/**
 * 模拟indexOf()
 * @param string
 * @param key
 * @returns {number}
 */
function str_indexOf (string, key) {
  var result = -1;
  for (var i = 0; i < string.length; i++) {
    if (i <= string.length - key.length) {
      var sub_key = '';
      for (var j = 0; j < key.length; j++) {
        sub_key += String(string[i + j]);
      }
      if (sub_key === key) {
        return i;
      }
    }
  }
  return result;
}
/**
* 字符串的替换删除新增
* @param string
* @param start
* @param deleteCount
* @returns {string}
*/
function str_splice (string, start, deleteCount) {
  var result = '';
  for (let i = 0; i < string.length; i++) {
    if ((i < start || i >= start + deleteCount) && start < string.length) {
      if (i === start + deleteCount) {
        for (let j = 3; j < arguments.length; j++) {
          result += arguments[j];
        }
      }
      result += string[i];
    }
  }
  if (start >= string.length) {
    for (let k = 3; k < arguments.length; k++) {
      string += arguments[k];
    }
    return string;
  }
  return result;
}


function sort (array, key, shunxu) {
  shuxing = String(key);
  for (var i = 0; i < array.length; i++) {
    for (var j = i + 1; j < array.length; j++) {
      if (shunxu === true) {
        if (array[i][key] > array[j][key]) {
          var temp = array[i];
          array[i] = array[j];
          array[j] = temp;
        }
      } else if (shunxu === false) {
        if (array[i][key] < array[j][key]) {
          var temp = array[i];
          array[i] = array[j];
          array[j] = temp;
        }
      }

    }
  }
  return array
}
/*
测试
// var arr = [2, 3, 1];
var arr = [{
  data: 1,
  age: 2
},
{
  data: 3,
  age: 3
},
{
  data: 2,
  age: 1
}]
var res = sort(arr, 'age', true);
console.log(res);
console.log(arr);
*/

/**
* 颠倒数组
* @param array
* @returns {[]}
*/
function arr_reverse (array) {
  var result = [];
  for (var i = array.length - 1; i >= 0; i--) {
    result[result.length] = array[i];
  }
  return result;
}

/**
* 从数组尾部删除元素
* @param array
* @returns {[]}
*/
function arr_pop (array) {
  var result = [];
  for (var i = 0; i < array.length - 1; i++) {
    result[result.length] = array[i];
  }
  array = result
  return result;
}

/**
* 数组转化为字符串
* @param array
* @returns {string}
*/
function arr_toString (array) {
  var string = '';
  for (var i = 0; i < array.length; i++) {
    if (i < array.length - 1) {
      string += array[i] + ',';
    } else {
      string += array[i];
    }
  }
  return string
}

/**
* 数组转化为字符串join()
* @param array
* @param key
* @returns {string}
*/
function arr_join (array, key) {
  var string = '';
  for (var i = 0; i < array.length; i++) {
    if (i < array.length - 1) {
      string += array[i] + key;
    } else {
      string += array[i];
    }
  }
  return string
}

/**
* 截取片段
* @param array
* @param start
* @param end
* @returns {[]}
*/
function arr_slice (array, start, end) {
  if (!Array.isArray(array)) {
    throw new Error('parms1 must be array');
  }
  if (typeof start !== 'number') {
    throw new Error('parms2 must be number');
  }
  if (typeof end !== "undefined") {
    if (typeof end != "number")
      throw new Error('parms3 must be number  or undefined ');
  }
  // if (typeof end !=='undefined') {
  //     throw new Error('parms3 must be number  or undefined ');
  // }
  var result = [];
  for (var i = start; i < array.length; i++) {
    if (end > i || end === undefined) {
      result[result.length] = array[i];
    }
  }
  return result;
}

/**
* 遍历数组
* @param arr
* @param callback
*/
function arr_forEach (arr, callback) {
  if (!Array.isArray(arr)) { throw new Error('param1 must be an array') }
  if (typeof callback !== 'function') { throw new Error('param2 must be an array') }
  for (let i = 0; i < arr.length; i++) {
    callback(arr[i], i, arr);
  }
}

/**
* 筛选数组
* @param arr
* @param callback
* @returns {[]}
*/
function arr_filter (arr, callback) {
  if (!Array.isArray(arr)) { throw new Error('param1 must be an array') };
  if (typeof callback !== 'function') { throw new Error('param2 must be an array') };
  var res = [];
  for (let i = 0; i < arr.length; i++) {
    var result = callback(arr[i], i, arr);
    if (result) {
      res[res.length] = arr[i];
    }
  }
  return res;
}

/**
* //模拟str.substr()
* @param string
* @param startIndex
* @param length
* @returns {string}
*/
// function str_substr(string,startIndex,length) {
//     var result = '';
//     for (let i = 0; i < length; i++) {
//         result+=string[startIndex+i];
//     }
//     return result
// }
function str_substr (string, startIndex, length) {
  var result = '';
  for (let i = 0; i < string.length; i++) {
    if (i >= startIndex && i < startIndex + length) {
      result += string[i];
    }
  }
  return result
}


/**
* 颠倒字符串
* @param str
* @returns {string}
*/
function rev (str) {
  var res = ''
  for (var i = str.length - 1; i >= 0; i--) {
    res += (str[i]);
  }
  return res;
}

/**
 * 1. 手写 call 方法
*/
Function.prototype.myCall = function (context, ...args) {
  if (typeof context === 'object' || typeof context === 'function') {
    context = context || window
  } else {
    context = Object.create(null)
  }
  // 为了避免函数名与上下文 (context) 的属性发生冲突
  // 利用 Symbol 的唯一性生成一个不重复的键
  let fn = Symbol('fn')
  context[fn] = this
  const result = context[fn](...args)
  // 调用函数后将对象上的函数删掉
  delete context[fn]
  return result
}
/*
测试
let person = { name: '一哥' }
function test (age, sex) {
  console.log(this.name, age, sex) // => 一哥 27 男
}
test.myCall(person, 27, '男')
*/

/**
 * 2. 手写 apply 方法
*/

Function.prototype.myApply = function (context, args) {
  if (typeof context === 'object' || typeof context === 'function') {
    context = context || window
  } else {
    context = Object.create(null)
  }
  let fn = Symbol('fn')
  context[fn] = this
  const result = context[fn](...args)
  delete context[fn]
  return result
}
/*
测试
let person = { name: '慧宝' }

function test (age, sex) {
  console.log(this.name, age, sex) // => 慧宝 25 女
}
test.myApply(person, [25, '女'])
*/

/**
 * 3. 手写 bind 方法
*/
Function.prototype.myBind = function (context, ...args) {
  let self = this // 谨记 this 表示调用 bind 函数
  let bindFn = function (...newArgs) {
    // 使用 call 指定 this
    // 当返回的绑定函数作为构造函数呗 new 调动，绑定的上下文指向实例对象 
    return self.call(this instanceof bindFn ? this : context || window,
      ...args, ...newArgs)
  }
  // 设置绑定函数的 prototype 为原函数的 prototype
  bindFn.prototype = Object.create(self.prototype)
  return bindFn
}
/*
测试
let person = { name: '一哥' }
function testBind (age, sex) {
  console.log(this.name, age, sex) // => 一哥 27 男
}

const test = testBind.myBind(person, 27)
test('男')
*/

/**
 * 4. 手写 new 操作符
*/
/*
* 1. 创建一个新的空对象
* 2. 将新的空对象的 __proto__ 属性指向原函数的 prototype
* 3. 将新的空对象绑定到此函数的 this 上
* 4. 返回新的空对象
*/
const myNew = function (Con, ...args) {
  let obj = {}
  // obj.__proto__ = Con.protitype // ES5
  Object.setPrototypeOf(obj, Con.prototype) // ES6
  const result = Con.apply(obj, args)
  return result instanceof Object ? result : obj
}
/*
测试
function Test (name, age) {
  this.name = name
  this.age = age
}

Test.prototype.sayHi = function () {
  console.log('Test-----实现 new 关键字')
}
const test = myNew(Test, '测试', 18)
console.log(test.name) // 测试
console.log(test.age) // 18
test.sayHi() // Test-----实现 new 关键字
*/

/**
 * 5. 手写 instanceof 方法
*/

/*
* 1. 获取类型的原型
* 2. 获取对象的原型
* 3. 一直循环判断对象的原型是否等于类型的原型，直到对象原型为 null，因为原型链最终为 null
*/
// const myInstanceof = function (left, right) {
//   let prototype = right.prototype // 获取类型的原型
//   let left = left.__proto__ // 获取对象的原型
//   while (true) {
//     if (left === null || left === undefined) return false
//     if (prototype === left) return true
//     left = left.__proto__
//   }
//   // let proto = Object.getPrototypeOf(left)
//   // while (true) {
//   //	if (proto == null) return false
//   //	if (proto == right.prototype) return true
//   //	proto = Object.getPrototypeOf(proto)
//   // }
// }
/**
 * 6. 手写 Object.create() 方法
*/
const myCreate = function (proto) {
  function Fn () { }
  Fn.prototype = proto
  Fn.prototype.constructor = Fn
  return new Fn()
}

/**
 * 7. 实现节流函数功能
 * 如果一个事件被频繁触发多次，节流函数可以按照固定的频率去执行对应的事件处理方法。函数节流保证一个事件一定时间内只执行一次。
 * 应用场景：
 * DOM 元素的拖拽功能实现（mousemove）
 * 搜索联想（keyup）
 * 滚动事件 scroll （只要页面滚动就会间隔一段时间判断一次）
 * 计算鼠标移动的距离（mousemove）
 * 射击游戏的 mousedown/keydown 事件（单位时间只能发射一颗子弹）
*/
const throttle = (fn, delay = 500) => { // 每隔一段时间触发一次，像水滴一样
  let flag = true
  return (...args) => {
    if (!flag) return;
    flag = false
    setTimeout(() => {
      fn.apply(this, args)
      flag = true
    }, delay)
  }
}
/**
 * 8. 实现防抖函数功能
 * 如果一个事件被频繁触发多次，并且触发的事件间隔过短，则防抖函数可以使得对应的事件。处理函数只执行最后触发的一次。
 * 应用场景
 * 手机号、邮箱输入验证
 * 搜索框搜索输入（只需要最后一次输入完后再发 Ajax 请求）
 * 窗口大小 resize 事件（只需要窗口调整完成后，计算窗口大小，防止重复渲染）
 * 滚动事件 scroll（只需要执行触发的最后一次滚动事件的处理程序）
 * 
*/
const debounce = (fn, delay = 500) => {
  let timer = null
  return (...args) => {
    // 如果事件被触发，清除timer并重新开始计时
    clearTimeout(timer)
    timer = setTimeout(() => {
      fn.apply(this, args)
    }, delay)
  }
}
